<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="theme-color" content="#0078E7"><meta name="author" content="yangbo.wang"><meta name="copyright" content="yangbo.wang"><meta name="generator" content="Hexo 5.3.0"><meta name="theme" content="hexo-theme-yun"><title>Python 元类 | Blog</title><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@900&amp;display=swap" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/star-markdown-css@0.1.22/dist/yun/yun-markdown.min.css"><script src="//at.alicdn.com/t/font_1140697_ed8vp4atwoj.js" async></script><script src="https://cdn.jsdelivr.net/npm/scrollreveal/dist/scrollreveal.min.js" defer></script><script>document.addEventListener("DOMContentLoaded", () => {
  [".post-card",".post-content img"].forEach((target)=> {
    ScrollReveal().reveal(target);
  })
});
</script><link rel="shortcut icon" type="image/svg+xml" href="/yun.svg"><link rel="mask-icon" href="/yun.svg" color="#0078E7"><link rel="alternate icon" href="/yun.ico"><link rel="preload" href="/css/hexo-theme-yun.css" as="style"><link rel="preload" href="/js/utils.js" as="script"><link rel="preload" href="/js/hexo-theme-yun.js" as="script"><link rel="prefetch" href="/js/sidebar.js" as="script"><link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin><script id="yun-config">
    const Yun = window.Yun || {};
    window.CONFIG = {"hostname":"example.com","root":"/","title":"你的月亮我的心","version":"1.4.0","mode":"dark","copycode":true,"page":{"isPost":true},"anonymous_image":"https://cdn.jsdelivr.net/gh/YunYouJun/cdn/img/avatar/none.jpg","say":{"api":"https://v1.hitokoto.cn","hitokoto":true},"fireworks":{"colors":["102, 167, 221","62, 131, 225","33, 78, 194"]}};
  </script><link rel="stylesheet" href="/css/hexo-theme-yun.css"><script src="/js/utils.js"></script><script src="/js/hexo-theme-yun.js"></script><meta name="description" content="类也是对象在大多数编程语言中，类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立： &gt;&gt;&gt; class ObjectCreator(object): …       pass … &gt;&gt;&gt; my_object &#x3D; ObjectCreator() &gt;&gt;&gt; print my_object &lt;__main__.Object">
<meta property="og:type" content="article">
<meta property="og:title" content="Python 元类">
<meta property="og:url" content="http://example.com/2021/01/17/001-%E5%85%83%E7%B1%BB/index.html">
<meta property="og:site_name" content="Blog">
<meta property="og:description" content="类也是对象在大多数编程语言中，类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立： &gt;&gt;&gt; class ObjectCreator(object): …       pass … &gt;&gt;&gt; my_object &#x3D; ObjectCreator() &gt;&gt;&gt; print my_object &lt;__main__.Object">
<meta property="og:locale" content="en_US">
<meta property="article:published_time" content="2021-01-17T04:32:52.217Z">
<meta property="article:modified_time" content="2021-01-17T07:33:05.786Z">
<meta property="article:author" content="yangbo.wang">
<meta property="article:tag" content="ObjectCreator">
<meta name="twitter:card" content="summary"><script src="/js/ui/mode.js"></script></head><body><script defer src="https://cdn.jsdelivr.net/npm/animejs@latest"></script><script defer src="/js/ui/fireworks.js"></script><canvas class="fireworks"></canvas><div class="container"><a class="sidebar-toggle hty-icon-button" id="menu-btn"><div class="hamburger hamburger--spin" type="button"><span class="hamburger-box"><span class="hamburger-inner"></span></span></div></a><div class="sidebar-toggle sidebar-overlay"></div><aside class="sidebar"><script src="/js/sidebar.js"></script><ul class="sidebar-nav"><li class="sidebar-nav-item sidebar-nav-toc hty-icon-button sidebar-nav-active" data-target="post-toc-wrap" title="Table of Contents"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-list-ordered"></use></svg></li><li class="sidebar-nav-item sidebar-nav-overview hty-icon-button" data-target="site-overview-wrap" title="Overview"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-passport-line"></use></svg></li></ul><div class="sidebar-panel" id="site-overview-wrap"><div class="site-info fix-top"><a class="site-author-avatar" href="/about/" title="yangbo.wang"><img width="96" loading="lazy" src="/Yun.png" alt="yangbo.wang"></a><div class="site-author-name"><a href="/about/">yangbo.wang</a></div><a class="site-name" href="/about/site.html">Blog</a><sub class="site-subtitle"></sub><div class="site-desciption"></div></div><nav class="site-state"><a class="site-state-item hty-icon-button icon-home" href="/" title="Home"><span class="site-state-item-icon"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-home-4-line"></use></svg></span></a><div class="site-state-item"><a href="/archives/" title="Archives"><span class="site-state-item-icon"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-archive-line"></use></svg></span><span class="site-state-item-count">8</span></a></div><div class="site-state-item"><a href="/categories/" title="Categories"><span class="site-state-item-icon"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-folder-2-line"></use></svg></span><span class="site-state-item-count">2</span></a></div><div class="site-state-item"><a href="/tags/" title="Tags"><span class="site-state-item-icon"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-price-tag-3-line"></use></svg></span><span class="site-state-item-count">9</span></a></div><a class="site-state-item hty-icon-button" href="/" title="文档"><span class="site-state-item-icon"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-settings-line"></use></svg></span></a></nav><hr style="margin-bottom:0.5rem"><div class="links-of-author"><a class="links-of-author-item hty-icon-button" rel="noopener" title="微信 A18309169837" target="_blank" style="color:orange"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-wechat-2-line"></use></svg></a><a class="links-of-author-item hty-icon-button" rel="noopener" title="QQ  1197213754" target="_blank" style="color:#12B7F5"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-qq-line"></use></svg></a><a class="links-of-author-item hty-icon-button" rel="noopener" href="https://gitee.com/wyb_001" title="GitHub" target="_blank" style="color:#6e5494"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-github-line"></use></svg></a><a class="links-of-author-item hty-icon-button" rel="noopener" title="E-Mail 1197213754@qq.com" target="_blank" style="color:#8E71C1"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-mail-line"></use></svg></a></div><hr style="margin:0.5rem 1rem"><div class="links"><a class="links-item hty-icon-button" href="/about/" title="关于我" style="color:dodgerblue"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-genderless-line"></use></svg></a></div></div><div class="sidebar-panel sidebar-panel-active" id="post-toc-wrap"><div class="post-toc"><div class="post-toc-content"><ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B1%BB%E4%B9%9F%E6%98%AF%E5%AF%B9%E8%B1%A1"><span class="toc-number">1.</span> <span class="toc-text">类也是对象</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E5%9C%B0%E5%88%9B%E5%BB%BA%E7%B1%BB"><span class="toc-number">2.</span> <span class="toc-text">动态地创建类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8type%E5%88%9B%E5%BB%BA%E7%B1%BB"><span class="toc-number">3.</span> <span class="toc-text">使用type创建类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8type%E5%88%9B%E5%BB%BA%E5%B8%A6%E6%9C%89%E5%B1%9E%E6%80%A7%E7%9A%84%E7%B1%BB"><span class="toc-number">4.</span> <span class="toc-text">使用type创建带有属性的类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8type%E5%88%9B%E5%BB%BA%E5%B8%A6%E6%9C%89%E6%96%B9%E6%B3%95%E7%9A%84%E7%B1%BB"><span class="toc-number">5.</span> <span class="toc-text">使用type创建带有方法的类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%B0%E5%BA%95%E4%BB%80%E4%B9%88%E6%98%AF%E5%85%83%E7%B1%BB%EF%BC%88%E7%BB%88%E4%BA%8E%E5%88%B0%E4%B8%BB%E9%A2%98%E4%BA%86%EF%BC%89"><span class="toc-number">6.</span> <span class="toc-text">到底什么是元类（终于到主题了）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#metaclass-%E5%B1%9E%E6%80%A7"><span class="toc-number">7.</span> <span class="toc-text">__metaclass__属性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%85%83%E7%B1%BB"><span class="toc-number">8.</span> <span class="toc-text">8. 自定义元类</span></a></li></ol></div></div></div></aside><main class="sidebar-translate" id="content"><div id="post"><article class="post-block" itemscope itemtype="https://schema.org/Article"><link itemprop="mainEntityOfPage" href="http://example.com/2021/01/17/001-%E5%85%83%E7%B1%BB/"><span hidden itemprop="author" itemscope itemtype="https://schema.org/Person"><meta itemprop="name" content="yangbo.wang"><meta itemprop="description"></span><span hidden itemprop="publisher" itemscope itemtype="https://schema.org/Organization"><meta itemprop="name" content="Blog"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">Python 元类</h1><div class="post-meta"><div class="post-time" style="display:inline-block"><span class="post-meta-item-icon"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-calendar-line"></use></svg></span> <time title="Created: 2021-01-17 12:32:52" itemprop="dateCreated datePublished" datetime="2021-01-17T12:32:52+08:00">2021-01-17</time></div><div class="post-classify"><span class="post-category"> <span class="post-meta-item-icon" style="margin-right:3px;"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-folder-line"></use></svg></span><span itemprop="about" itemscope itemtype="https://schema.org/Thing"><a class="category" href="/categories/Python/" style="--text-color:var(--hty-text-color)" itemprop="url" rel="index"><span itemprop="text">Python</span></a></span></span><span class="post-tag"><span class="post-meta-divider">-</span><a class="tag" href="/tags/ObjectCreator/" style="--text-color:var(--hty-text-color)"><span class="post-meta-item-icon"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-price-tag-3-line"></use></svg></span><span class="tag-name">ObjectCreator</span></a></span></div></div></header><section class="post-body" itemprop="articleBody"><div class="post-content markdown-body" style="--smc-primary:#0078E7;"><h3 id="类也是对象"><a href="#类也是对象" class="headerlink" title="类也是对象"></a>类也是对象</h3><p>在大多数编程语言中，类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立：</p>
<pre><code>&gt;&gt;&gt; class ObjectCreator(object):
…       pass
…
&gt;&gt;&gt; my_object = ObjectCreator()
&gt;&gt;&gt; print my_object
&lt;__main__.ObjectCreator object at 0x8974f2c&gt;  </code></pre>
<p>但是，Python中的类还远不止如此。类同样也是一种对象。是的，没错，就是对象。只要你使用关键字class，Python解释器在执行的时候就会创建一个对象。</p>
<p>下面的代码段：</p>
<pre><code>&gt;&gt;&gt; class ObjectCreator(object):
…       pass
…  </code></pre>
<p>将在内存中创建一个对象，名字就是ObjectCreator。这个对象（类对象ObjectCreator）拥有创建对象（实例对象）的能力。但是，它的本质仍然是一个对象，于是乎你可以对它做如下的操作：</p>
<p>1.你可以将它赋值给一个变量<br>2.你可以拷贝它<br>3.你可以为它增加属性<br>4.你可以将它作为函数参数进行传递  </p>
<p>下面是示例：</p>
<pre><code>&gt;&gt;&gt; print ObjectCreator     # 你可以打印一个类，因为它其实也是一个对象
&lt;class &#39;__main__.ObjectCreator&#39;&gt;
&gt;&gt;&gt; def echo(o):
…       print o
…
&gt;&gt;&gt; echo(ObjectCreator)                 # 你可以将类做为参数传给函数
&lt;class &#39;__main__.ObjectCreator&#39;&gt;
&gt;&gt;&gt; print hasattr(ObjectCreator, &#39;new_attribute&#39;)
Fasle
&gt;&gt;&gt; ObjectCreator.new_attribute = &#39;foo&#39; #  你可以为类增加属性
&gt;&gt;&gt; print hasattr(ObjectCreator, &#39;new_attribute&#39;)
True
&gt;&gt;&gt; print ObjectCreator.new_attribute
foo
&gt;&gt;&gt; ObjectCreatorMirror = ObjectCreator # 你可以将类赋值给一个变量
&gt;&gt;&gt; print ObjectCreatorMirror()
&lt;__main__.ObjectCreator object at 0x8997b4c&gt;  </code></pre>
<h3 id="动态地创建类"><a href="#动态地创建类" class="headerlink" title="动态地创建类"></a>动态地创建类</h3><p>因为类也是对象，你可以在运行时动态的创建它们，就像其他任何对象一样。首先，你可以在函数中创建类，使用class关键字即可。</p>
<pre><code>&gt;&gt;&gt; def choose_class(name):
…       if name == &#39;foo&#39;:
…           class Foo(object):
…               pass
…           return Foo     # 返回的是类，不是类的实例
…       else:
…           class Bar(object):
…               pass
…           return Bar
…
&gt;&gt;&gt; MyClass = choose_class(&#39;foo&#39;)
&gt;&gt;&gt; print MyClass              # 函数返回的是类，不是类的实例
&lt;class &#39;__main__&#39;.Foo&gt;
&gt;&gt;&gt; print MyClass()            # 你可以通过这个类创建类实例，也就是对象
&lt;__main__.Foo object at 0x89c6d4c&gt;  </code></pre>
<p>但这还不够动态，因为你仍然需要自己编写整个类的代码。由于类也是对象，所以它们必须是通过什么东西来生成的才对。当你使用class关键字时，Python解释器自动创建这个对象。但就和Python中的大多数事情一样，Python仍然提供给你手动处理的方法。</p>
<p>还记得内建函数type吗？这个古老但强大的函数能够让你知道一个对象的类型是什么，就像这样：</p>
<pre><code>&gt;&gt;&gt; print type(1) #数值的类型
&lt;type &#39;int&#39;&gt;
&gt;&gt;&gt; print type(&quot;1&quot;) #字符串的类型
&lt;type &#39;str&#39;&gt;
&gt;&gt;&gt; print type(ObjectCreator()) #实例对象的类型
&lt;class &#39;__main__.ObjectCreator&#39;&gt;
&gt;&gt;&gt; print type(ObjectCreator) #类的类型
&lt;type &#39;type&#39;&gt;  </code></pre>
<p>仔细观察上面的运行结果，发现使用type对ObjectCreator查看类型是，答案为type， 是不是有些惊讶。。。看下面</p>
<h3 id="使用type创建类"><a href="#使用type创建类" class="headerlink" title="使用type创建类"></a>使用type创建类</h3><p>type还有一种完全不同的功能，动态的创建类。</p>
<p>type可以接受一个类的描述作为参数，然后返回一个类。（要知道，根据传入参数的不同，同一个函数拥有两种完全不同的用法是一件很傻的事情，但这在Python中是为了保持向后兼容性）</p>
<p>type可以像这样工作：</p>
<p>type(类名, 由父类名称组成的元组（针对继承的情况，可以为空），包含属性的字典（名称和值）)</p>
<p>比如下面的代码：</p>
<pre><code>In [2]: class Test: #定义了一个Test类
   ...:     pass
   ...:
In [3]: Test() #创建了一个Test类的实例对象
Out[3]: &lt;__main__.Test at 0x10d3f8438&gt;
可以手动像这样创建：

Test2 = type(&quot;Test2&quot;,(),&#123;&#125;) #定了一个Test2类
In [5]: Test2() #创建了一个Test2类的实例对象
Out[5]: &lt;__main__.Test2 at 0x10d406b38&gt;  </code></pre>
<p>我们使用”Test2”作为类名，并且也可以把它当做一个变量来作为类的引用。类和变量是不同的，这里没有任何理由把事情弄的复杂。即type函数中第1个实参，也可以叫做其他的名字，这个名字表示类的名字</p>
<pre><code>In [23]: MyDogClass = type(&#39;MyDog&#39;, (), &#123;&#125;)

In [24]: print MyDogClass
&lt;class &#39;__main__.MyDog&#39;&gt;  </code></pre>
<p>使用help来测试这2个类</p>
<pre><code>In [10]: help(Test) #用help查看Test类

Help on class Test in module __main__:

class Test(builtins.object)
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)
In [8]: help(Test2) #用help查看Test2类

Help on class Test2 in module __main__:

class Test2(builtins.object)
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)  </code></pre>
<h3 id="使用type创建带有属性的类"><a href="#使用type创建带有属性的类" class="headerlink" title="使用type创建带有属性的类"></a>使用type创建带有属性的类</h3><p>type 接受一个字典来为类定义属性，因此</p>
<pre><code>   &gt;&gt;&gt; Foo = type(&#39;Foo&#39;, (), &#123;&#39;bar&#39;:True&#125;)</code></pre>
<p>可以翻译为：</p>
<pre><code>&gt;&gt;&gt; class Foo(object):
…       bar = True</code></pre>
<p>并且可以将Foo当成一个普通的类一样使用：</p>
<pre><code>&gt;&gt;&gt; print Foo
&lt;class &#39;__main__.Foo&#39;&gt;
&gt;&gt;&gt; print Foo.bar
True
&gt;&gt;&gt; f = Foo()
&gt;&gt;&gt; print f
&lt;__main__.Foo object at 0x8a9b84c&gt;
&gt;&gt;&gt; print f.bar
True  </code></pre>
<p>当然，你可以向这个类继承，所以，如下的代码：</p>
<pre><code>&gt;&gt;&gt; class FooChild(Foo):
…       pass  </code></pre>
<p>就可以写成：</p>
<pre><code>&gt;&gt;&gt; FooChild = type(&#39;FooChild&#39;, (Foo,),&#123;&#125;)
&gt;&gt;&gt; print FooChild
&lt;class &#39;__main__.FooChild&#39;&gt;
&gt;&gt;&gt; print FooChild.bar   # bar属性是由Foo继承而来
True  </code></pre>
<p>注意：</p>
<ul>
<li>type的第2个参数，元组中是父类的名字，而不是字符串</li>
<li>添加的属性是类属性，并不是实例属性  </li>
</ul>
<h3 id="使用type创建带有方法的类"><a href="#使用type创建带有方法的类" class="headerlink" title="使用type创建带有方法的类"></a>使用type创建带有方法的类</h3><p>最终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了。</p>
<p>添加实例方法</p>
<pre><code>In [46]: def echo_bar(self): #定义了一个普通的函数
    ...:     print(self.bar)
    ...:

In [47]: FooChild = type(&#39;FooChild&#39;, (Foo,), &#123;&#39;echo_bar&#39;: echo_bar&#125;) #让FooChild类中的echo_bar属性，指向了上面定义的函数

In [48]: hasattr(Foo, &#39;echo_bar&#39;) #判断Foo类中，是否有echo_bar这个属性
Out[48]: False

In [49]:

In [49]: hasattr(FooChild, &#39;echo_bar&#39;) #判断FooChild类中，是否有echo_bar这个属性
Out[49]: True

In [50]: my_foo = FooChild()

In [51]: my_foo.echo_bar()
True</code></pre>
<p>添加静态方法</p>
<pre><code>In [36]: @staticmethod
    ...: def testStatic():
    ...:     print(&quot;static method ....&quot;)
    ...:

In [37]: Foochild = type(&#39;Foochild&#39;, (Foo,), &#123;&quot;echo_bar&quot;:echo_bar, &quot;testStatic&quot;:
    ...: testStatic&#125;)

In [38]: fooclid = Foochild()

In [39]: fooclid.testStatic
Out[39]: &lt;function __main__.testStatic&gt;

In [40]: fooclid.testStatic()
static method ....

In [41]: fooclid.echo_bar()
True</code></pre>
<p>添加类方法</p>
<pre><code>In [42]: @classmethod
    ...: def testClass(cls):
    ...:     print(cls.bar)
    ...:

In [43]:

In [43]: Foochild = type(&#39;Foochild&#39;, (Foo,), &#123;&quot;echo_bar&quot;:echo_bar, &quot;testStatic&quot;:
    ...: testStatic, &quot;testClass&quot;:testClass&#125;)

In [44]:

In [44]: fooclid = Foochild()

In [45]: fooclid.testClass()
True  </code></pre>
<p>你可以看到，在Python中，类也是对象，你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情，而这就是通过元类来实现的。</p>
<h3 id="到底什么是元类（终于到主题了）"><a href="#到底什么是元类（终于到主题了）" class="headerlink" title="到底什么是元类（终于到主题了）"></a>到底什么是元类（终于到主题了）</h3><p>元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象，不是吗？但是我们已经学习到了Python中的类也是对象。</p>
<p>元类就是用来创建这些类（对象）的，元类就是类的类，你可以这样理解为：</p>
<p>MyClass = MetaClass() #使用元类创建出一个对象，这个对象称为“类”<br>MyObject = MyClass() #使用“类”来创建出实例对象<br>你已经看到了type可以让你像这样做：</p>
<p>MyClass = type(‘MyClass’, (), {})<br>这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢？好吧，我猜这是为了和str保持一致性，str是用来创建字符串对象的类，而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西，注意，我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象，而且它们都是从一个类创建而来，这个类就是type。</p>
<pre><code>&gt;&gt;&gt; age = 35
&gt;&gt;&gt; age.__class__
&lt;type &#39;int&#39;&gt;
&gt;&gt;&gt; name = &#39;bob&#39;
&gt;&gt;&gt; name.__class__
&lt;type &#39;str&#39;&gt;
&gt;&gt;&gt; def foo(): pass
&gt;&gt;&gt;foo.__class__
&lt;type &#39;function&#39;&gt;
&gt;&gt;&gt; class Bar(object): pass
&gt;&gt;&gt; b = Bar()
&gt;&gt;&gt; b.__class__
&lt;class &#39;__main__.Bar&#39;&gt;  </code></pre>
<p>现在，对于任何一个__class__的__class__属性又是什么呢？</p>
<pre><code>&gt;&gt;&gt; a.__class__.__class__
&lt;type &#39;type&#39;&gt;
&gt;&gt;&gt; age.__class__.__class__
&lt;type &#39;type&#39;&gt;
&gt;&gt;&gt; foo.__class__.__class__
&lt;type &#39;type&#39;&gt;
&gt;&gt;&gt; b.__class__.__class__
&lt;type &#39;type&#39;&gt;  </code></pre>
<p>因此，元类就是创建类这种对象的东西。type就是Python的内建元类，当然了，你也可以创建自己的元类。</p>
<h3 id="metaclass-属性"><a href="#metaclass-属性" class="headerlink" title="__metaclass__属性"></a>__metaclass__属性</h3><p>你可以在定义一个类的时候为其添加__metaclass__属性。</p>
<pre><code>class Foo(object):
    __metaclass__ = something…  

...省略...  </code></pre>
<p>如果你这么做了，Python就会用元类来创建类Foo。小心点，这里面有些技巧。你首先写下class Foo(object)，但是类Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性，如果找到了，Python就会用它来创建类Foo，如果没有找到，就会用内建的type来创建这个类。把下面这段话反复读几次。当你写如下代码时 :</p>
<pre><code>class Foo(Bar):
    pass  </code></pre>
<p>Python做了如下的操作：</p>
<p>1.Foo中有_<em>metaclass_<em>这个属性吗？如果是，Python会通过__metaclass__创建一个名字为Foo的类(对象)<br>2.如果Python没有找到__metaclass__，它会继续在Bar（父类）中寻找__metaclass__属性，并尝试做和前面同样的操作。<br>3.如果Python在任何父类中都找不到__metaclass__，它就会在模块层次中去寻找__metaclass__，并尝试做同样的操作。<br>4.如果还是找不到__metaclass\</em></em>,Python就会用内置的type来创建这个类对象。    </p>
<p>现在的问题就是，你可以在__metaclass__中放置些什么代码呢？答案就是：可以创建一个类的东西。那么什么可以用来创建一个类呢？type，或者任何使用到type或者子类化type的东东都可以。</p>
<h3 id="8-自定义元类"><a href="#8-自定义元类" class="headerlink" title="8. 自定义元类"></a>8. 自定义元类</h3><p>元类的主要目的就是为了当创建类时能够自动地改变类。通常，你会为API做这样的事情，你希望可以创建符合当前上下文的类。</p>
<p>假想一个很傻的例子，你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到，但其中一种就是通过在模块级别设定__metaclass__。采用这种方法，这个模块中的所有类都会通过这个元类来创建，我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。</p>
<p>幸运的是，__metaclass__实际上可以被任意调用，它并不需要是一个正式的类。所以，我们这里就先以一个简单的函数作为例子开始。</p>
<p>python2中</p>
<pre><code>#-*- coding:utf-8 -*-
def upper_attr(future_class_name, future_class_parents, future_class_attr):

    #遍历属性字典，把不是__开头的属性名字变为大写
    newAttr = &#123;&#125;
    for name,value in future_class_attr.items():
        if not name.startswith(&quot;__&quot;):
            newAttr[name.upper()] = value

    #调用type来创建一个类
    return type(future_class_name, future_class_parents, newAttr)

class Foo(object):
    __metaclass__ = upper_attr #设置Foo类的元类为upper_attr
    bar = &#39;bip&#39;

print(hasattr(Foo, &#39;bar&#39;))
print(hasattr(Foo, &#39;BAR&#39;))

f = Foo()
print(f.BAR)  </code></pre>
<p>python3中</p>
<pre><code>#-*- coding:utf-8 -*-
def upper_attr(future_class_name, future_class_parents, future_class_attr):

    #遍历属性字典，把不是__开头的属性名字变为大写
    newAttr = &#123;&#125;
    for name,value in future_class_attr.items():
        if not name.startswith(&quot;__&quot;):
            newAttr[name.upper()] = value

    #调用type来创建一个类
    return type(future_class_name, future_class_parents, newAttr)

class Foo(object, metaclass=upper_attr):
    bar = &#39;bip&#39;

print(hasattr(Foo, &#39;bar&#39;))
print(hasattr(Foo, &#39;BAR&#39;))

f = Foo()
print(f.BAR)  </code></pre>
<p>现在让我们再做一次，这一次用一个真正的class来当做元类。</p>
<pre><code>#coding=utf-8

class UpperAttrMetaClass(type):
    # __new__ 是在__init__之前被调用的特殊方法
    # __new__是用来创建对象并返回之的方法
    # 而__init__只是用来将传入的参数初始化给对象
    # 你很少用到__new__，除非你希望能够控制对象的创建
    # 这里，创建的对象是类，我们希望能够自定义它，所以我们这里改写__new__
    # 如果你希望的话，你也可以在__init__中做些事情
    # 还有一些高级的用法会涉及到改写__call__特殊方法，但是我们这里不用
    def __new__(cls, future_class_name, future_class_parents, future_class_attr):
        #遍历属性字典，把不是__开头的属性名字变为大写
        newAttr = &#123;&#125;
        for name,value in future_class_attr.items():
            if not name.startswith(&quot;__&quot;):
                newAttr[name.upper()] = value

        # 方法1：通过&#39;type&#39;来做类对象的创建
        # return type(future_class_name, future_class_parents, newAttr)

        # 方法2：复用type.__new__方法
        # 这就是基本的OOP编程，没什么魔法
        # return type.__new__(cls, future_class_name, future_class_parents, newAttr)

        # 方法3：使用super方法
        return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr)

        #python2的用法
        class Foo(object):
            __metaclass__ = UpperAttrMetaClass
            bar = &#39;bip&#39;

        # python3的用法
        # class Foo(object, metaclass = UpperAttrMetaClass):
        #     bar = &#39;bip&#39;

        print(hasattr(Foo, &#39;bar&#39;))
        # 输出: False
        print(hasattr(Foo, &#39;BAR&#39;))
        # 输出:True

        f = Foo()
        print(f.BAR)
        # 输出:&#39;bip&#39;  </code></pre>
<p>就是这样，除此之外，关于元类真的没有别的可说的了。但就元类本身而言，它们其实是很简单的：</p>
<p>1.拦截类的创建<br>2.修改类<br>3.返回修改之后的类  </p>
<p><strong>究竟为什么要使用元类？</strong></p>
<p>现在回到我们的大主题上来，究竟是为什么你会去使用这样一种容易出错且晦涩的特性？好吧，一般来说，你根本就用不上它：</p>
<p>“元类就是深度的魔法，99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类，那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么，而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters</p>
</div><ul class="post-copyright"><li class="post-copyright-author"><strong>Post author: </strong>yangbo.wang</li><li class="post-copyright-link"><strong>Post link: </strong><a href="http://example.com/2021/01/17/001-%E5%85%83%E7%B1%BB/" title="Python 元类">http://example.com/2021/01/17/001-%E5%85%83%E7%B1%BB/</a></li><li class="post-copyright-license"><strong>Copyright Notice: </strong>All articles in this blog are licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" target="_blank" rel="noopener" title="CC BY-NC-SA 4.0 "><svg class="icon"><use xlink:href="#icon-creative-commons-line"></use></svg><svg class="icon"><use xlink:href="#icon-creative-commons-by-line"></use></svg><svg class="icon"><use xlink:href="#icon-creative-commons-nc-line"></use></svg><svg class="icon"><use xlink:href="#icon-creative-commons-sa-line"></use></svg></a> unless otherwise stated.</li></ul></section></article><div class="post-nav"><div class="post-nav-item"><a class="post-nav-prev" href="/2021/01/17/request%E5%AF%B9%E8%B1%A1/" rel="prev" title="HttpReqeust对象"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-arrow-left-s-line"></use></svg><span class="post-nav-text">HttpReqeust对象</span></a></div><div class="post-nav-item"><a class="post-nav-next" href="/2021/01/17/hello-world/" rel="next" title="Hexo"><span class="post-nav-text">Hexo</span><svg class="icon" aria-hidden="true"><use xlink:href="#icon-arrow-right-s-line"></use></svg></a></div></div></div></main><footer class="sidebar-translate" id="footer"><div class="copyright"><span>&copy; 2019 – 2021 </span><span class="with-love" id="animate"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-cloud-line"></use></svg></span><span class="author"> yangbo.wang</span></div><div class="powered"><span>Powered by <a href="https://hexo.io" target="_blank" rel="noopener">Hexo</a> v5.3.0</span><span class="footer-separator">|</span><span>Theme - <a rel="noopener" href="https://github.com/YunYouJun/hexo-theme-yun" target="_blank"><span>Yun</span></a> v1.4.0</span></div></footer><a class="hty-icon-button" id="goUp" aria-label="back-to-top" href="#"><svg class="icon" aria-hidden="true"><use xlink:href="#icon-arrow-up-s-line"></use></svg><svg class="progress-circle-container" viewBox="0 0 100 100"><circle class="progress-circle" id="progressCircle" cx="50" cy="50" r="48" fill="none" stroke="#0078E7" stroke-width="2" stroke-linecap="round"></circle></svg></a></div></body></html>